/* omega copyright (C) by Laurence Raphael Brothers, 1987,1988,1989 */
/* spell.c */
/* functions having to do with spellcasting */

#include "glob.h"


void s_wish()
{
  if (random_range(100) > Player.iq+Player.pow+Player.level) {
    mprint("Your concentration is flawed!");
    mprint("The spell energy backfires!");
    p_damage(random_range(Spells[S_WISH].powerdrain),
	     UNSTOPPABLE,
	     "a backfired wish spell");
  }
  else {
    wish(0);
    if (Spells[S_WISH].known) {
      mprint("The power of the spell is too much for you to withstand!");
      mprint("All memory of the spell is expunged from your brain.");
      Spells[S_WISH].known = FALSE;
    }
  }
}

void s_firebolt()
{
  int x=Player.x,y=Player.y;
  setspot(&x,&y);
  fbolt(Player.x,Player.y,x,y,Player.dex*2+Player.level,Player.level*10+10);
}

void s_missile()
{
  int x=Player.x,y=Player.y;
  setspot(&x,&y);
  nbolt(Player.x,Player.y,x,y,Player.dex*2+Player.level,Player.level*3+3);
}

void s_teleport()
{
  p_teleport(0);
}

void s_disrupt()
{
  int x=Player.x,y=Player.y;
  setspot(&x,&y);
  disrupt(x,y,Player.level*10+25);
}


void s_disintegrate()
{
  int x=Player.x,y=Player.y;
  setspot(&x,&y);
  disintegrate(x,y);
}


void s_sleep()
{
  sleep_monster(0);
}

void s_heal()
{
  heal(3);
}

void s_dispel()
{
  dispel((Player.level+Player.maxpow)/10); 
}

void s_breathe()
{
  breathe(0);
}

void s_invisible()
{
  invisible(0);
}


void s_warp()
{
  warp(1);
}

void s_enchant()
{
  enchant(1);
}


void s_bless()
{
  bless(0);
}


void s_restore()
{
  recover_stat(0);
}


void s_cure()
{
  cure(0);
}


void s_truesight()
{
  truesight(0);
}


void s_hellfire()
{
  int x=Player.x,y=Player.y;
  setspot(&x,&y);
  hellfire(x,y,0);
}


void s_knowledge()
{
  knowledge(0);
}


void s_hero()
{
  hero(0);
}

/* spell takes longer and longer to work deeper into dungeon */
void s_return()
{
  mprint("You hear a whine as your spell begins to charge up.");
  Player.status[RETURNING] = 
    ((Current_Environment == Current_Dungeon) ? difficulty() : 1);
}

void s_desecrate()
{
  sanctify(-1);
}


void s_haste()
{
  haste(0);
}


void s_summon()
{
  summon(0,-1);
}


void s_sanctuary()
{
  sanctuary();
}

void s_sanctify()
{
  sanctify(1);
}


void s_accuracy()
{
  accuracy(0);
}

void s_fear()
{
  int x = Player.x,y=Player.y;
  setspot(&x,&y);
  inflict_fear(x,y);
}


/* Has all kinds of effects in different circumstances.
   Eventually will be more interesting */
void s_ritual()
{
  pob symbol;
  int i,roomno;
  int x,y;

  mprint("You begin your ritual....");
  mprint("You enter a deep trance. Time Passes...");
  setgamestatus(SKIP_PLAYER);
  time_clock(FALSE);
  setgamestatus(SKIP_PLAYER);
  time_clock(FALSE);
  setgamestatus(SKIP_PLAYER);
  time_clock(FALSE);
  setgamestatus(SKIP_PLAYER);
  time_clock(FALSE);
  setgamestatus(SKIP_PLAYER);
  time_clock(FALSE);
  if (RitualHour == hour())
    mprint("Your mental fatigue prevents from completing the ritual!");
  else if (random_range(100) > Player.iq+Player.pow+Player.level)
    mprint("Your concentration was broken -- the ritual fails!");
  else {
    mprint("You charge the ritual with magical energy and focus your will.");
    mprint("Time Passes...");
    setgamestatus(SKIP_PLAYER);
    time_clock(FALSE);
    setgamestatus(SKIP_PLAYER);
    time_clock(FALSE);
    setgamestatus(SKIP_PLAYER);
    time_clock(FALSE);
    setgamestatus(SKIP_PLAYER);
    time_clock(FALSE);
    setgamestatus(SKIP_PLAYER);
    time_clock(FALSE);
    RitualHour = hour();
    /* set of random conditions for different ritual effects */
    if (Current_Environment == E_CITY) {
      mprint("Flowing waves of mystical light congeal all around you.");
      mprint("'Like wow, man! Colors!'");
      mprint("Appreciative citizens throw you spare change.");
      Player.cash +=random_range(50);
    }
    else if ((roomno=Level->site[Player.x][Player.y].roomnumber) >= 0) {
      if (RitualRoom == roomno)
	mprint("For some reason the ritual doesn't work this time...");
      else {
	RitualRoom = roomno;
	switch (RitualRoom) {
	case ROOMBASE+9: /* ransacked treasure chamber */
	  mprint("Your spell sets off frenetic growth all around you!");
	  for(i=0;i<8;i++){
	    Level->site[Player.x+Dirs[0][i]][Player.y+Dirs[1][i]].locchar =
	      HEDGE;
	    Level->site[Player.x+Dirs[0][i]][Player.y+Dirs[1][i]].p_locf =
	      L_TRIFID;
	    lset(Player.x+Dirs[0][i], Player.y+Dirs[1][i], CHANGED);
	  }
	  break;
	case ROOMBASE+13: /* harem */
	case ROOMBASE+22: /* boudoir */
	  mprint("A secret panel opens next to the bed....");
	  if (random_range(2))
	    summon(0,ML4+6); /* succubus/incubus */
	  else summon(0,ML4+7); /* satyr/nymph */
	  break;
	case ROOMBASE+26: /*shrine to high magic */
	  mprint("A storm of mana coaelesces around you.");
	  mprint("You are buffeted by bursts of random magic.");
	  p_damage(random_range(Player.pow),UNSTOPPABLE,"high magic");
	  mprint("Continue ritual? Could be dangerous.... [yn] ");
	  if (ynq()=='y') s_wish();
	  else mprint("The mana fades away to nothingness.");
	  x = Player.x;
	  y = Player.y;
	  while (x >= 0 && Level->site[x - 1][y].roomnumber == ROOMBASE+26)
	    x--;
	  while (y >= 0 && Level->site[x][y - 1].roomnumber == ROOMBASE+26)
	    y--;
	  for (i = 0; Level->site[x][y].roomnumber == ROOMBASE+26;) {
	    Level->site[x][y].roomnumber = RS_ZORCH;
	    x++;
	    i++;
	    if (Level->site[x][y].roomnumber != ROOMBASE+26) {
	      x -= i;
	      i = 0;
	      y++;
	    }
	  }
	  lset(Player.x, Player.y, CHANGED);
	  break;
	case ROOMBASE+27: /* magician's lab */
	  mprint("Your magical activity sets off a latent spell in the lab!");
	  cast_spell(random_range(NUMSPELLS));
	  break;
	case ROOMBASE+28: /* pentagram room */
	  mprint("A smoky form begins to coalesce....");
	  summon(-1,-1);
	  mprint("Fortunately, it seems confined to the pentagram.");
	  m_status_reset(Level->mlist->m,MOBILE);
	  break;
	case ROOMBASE+29: /* blue omega room */
	  mprint("The Lords of Destiny look upon you....");
	  if (Player.level > 10) {
	    mprint("A curtain of blue flames leaps up from the omega.");
	    morewait();
	    l_adept();
	  }
	  else {
	    if (Player.patron == DESTINY) {
	      mprint("Your patrons take pity on you.");
	      if ((Player.rank[PRIESTHOOD]<SPRIEST) &&
		  (! find_item(&symbol,ARTIFACTID+19,-1))) {
		symbol = ((pob) checkmalloc(sizeof(objtype)));
		*symbol = Objects[ARTIFACTID+19];
		symbol->known = 2;
		symbol->charge = 17;
		gain_item(symbol);
		mprint("You feel uplifted.");
	      }
	      else gain_experience(min(1000,Player.xp));
	    }
	    else if (random_range(3)==1) {
	      mprint("You feel Fated.");
	      gain_experience(Player.level*Player.level*10);
	      Player.hp = Player.maxhp;
	    }
	    else if (random_range(2)) {
	      mprint("You feel Doomed.");
	      Player.hp = 1;
	      Player.mana = 0;
	      Player.xp = 0;
	    }
	    else mprint("The Lords of Destiny laugh at you!");
	  }
	  break;
	default:
	  mprint("Well, not much effect. Chalk it up to experience.");
	  gain_experience(Player.level*5);
	  break;
	}
      }
    }
    else {
      if (RitualRoom == Level->site[Player.x][Player.y].roomnumber)
	mprint("The ritual fails for some unexplainable reason.");
      else {
	mprint("The ritual seems to be generating some spell effect.");
	RitualRoom = Level->site[Player.x][Player.y].roomnumber;
	switch (RitualRoom) {
	case RS_WALLSPACE:
	  shadowform();
	  break;
	case RS_CORRIDOR:
	  haste(0);
	  break;
	case RS_PONDS:
	  breathe(0);
	  break;
	case RS_ADEPT:
	  hero(1);
	  break;
	default:
	  mprint("The ritual doesn't seem to produce any tangible results...");
	  gain_experience(Player.level*6);
	}
      }
    }
  }
}



void s_apport()
{
  apport(0);
}

void s_shadowform()
{
  shadowform();
}

void s_alert()
{
  alert(0);
}

void s_regenerate()
{
  regenerate(0);
}

void s_clairvoyance()
{
  clairvoyance(10);
}

void s_drain()
{
  drain(0);
}

void s_levitate()
{
  levitate(0);
}

void s_polymorph()
{
  polymorph(0);
}


/* lball spell */
void s_lball()
{
  int x=Player.x,y=Player.y;
  setspot(&x,&y);
  lball(Player.x,Player.y,x,y,Player.level*10+10);
}

void s_identify()
{
  identify(0);
}

void s_objdet()
{
  objdet(1);
}

void s_mondet()
{
  mondet(1);
}


/* select a spell to cast */
int getspell()
{
  int spell= ABORT - 1;

  do {
    mprint("Cast Spell: [type spell abbrev, ?, or ESCAPE]: ");
    spell = spellparse();
  } while (spell < ABORT);
  return(spell);
}


char *spellid(id)
int id;
{
  switch(id) {
    case S_MON_DET:return("monster detection");
    case S_OBJ_DET:return("object detection");
    case S_IDENTIFY:return("identification");
    case S_FIREBOLT:return("firebolt");
    case S_LBALL:return("ball lightning");
    case S_SLEEP:return("sleep");
    case S_DISRUPT:return("disrupt");
    case S_DISINTEGRATE:return("disintegrate");
    case S_TELEPORT:return("teleport");
    case S_MISSILE:return("magic missile");
    case S_HEAL:return("healing");
    case S_DISPEL:return("dispelling");
    case S_BREATHE:return("breathing");
    case S_INVISIBLE:return("invisibility");
    case S_WARP:return("the warp");
    case S_ENCHANT:return("enchantment");
    case S_BLESS:return("blessing");
    case S_RESTORE:return("restoration");
    case S_CURE:return("curing");
    case S_TRUESIGHT:return("true sight");
    case S_HELLFIRE:return("hellfire");
    case S_KNOWLEDGE:return("self knowledge");
    case S_HERO:return("heroism");
    case S_RETURN:return("return");
    case S_DESECRATE:return("desecration");
    case S_HASTE:return("haste");
    case S_SUMMON:return("summoning");
    case S_SANCTUARY:return("sanctuary");
    case S_ACCURACY:return("accuracy");
    case S_RITUAL:return("ritual magic");
    case S_APPORT:return("apportation");
    case S_SHADOWFORM:return("shadow form");
    case S_ALERT:return("alertness");
    case S_REGENERATE:return("regeneration");
    case S_SANCTIFY:return("sanctification");
    case S_CLAIRVOYANCE:return("clairvoyance");
    case S_DRAIN:return("energy drain");
    case S_LEVITATE:return("levitate");
    case S_POLYMORPH:return("polymorph");
    case S_FEAR:return("fear");
    case S_WISH:return("wishing");
    default:return("???");
  }      
}



void initspells()
{
  int i;

  for (i=0; i<NUMSPELLS; i++)
    Spells[i].known = FALSE;

  Spells[S_MON_DET].powerdrain = 3;
  Spells[S_MON_DET].id = S_MON_DET;

  Spells[S_OBJ_DET].powerdrain = 3;
  Spells[S_OBJ_DET].id = S_OBJ_DET;

  Spells[S_IDENTIFY].powerdrain = 10;
  Spells[S_IDENTIFY].id = S_IDENTIFY;

  Spells[S_FIREBOLT].powerdrain = 20;
  Spells[S_FIREBOLT].id = S_FIREBOLT;

  Spells[S_SLEEP].powerdrain = 15;
  Spells[S_SLEEP].id = S_SLEEP;

  Spells[S_LBALL].powerdrain = 25;
  Spells[S_LBALL].id = S_LBALL;

  Spells[S_TELEPORT].powerdrain = 20;
  Spells[S_TELEPORT].id = S_TELEPORT;

  Spells[S_DISRUPT].powerdrain = 30;
  Spells[S_DISRUPT].id = S_DISRUPT;

  Spells[S_DISINTEGRATE].powerdrain = 40;
  Spells[S_DISINTEGRATE].id = S_DISINTEGRATE;

  Spells[S_MISSILE].powerdrain = 10;
  Spells[S_MISSILE].id = S_MISSILE;

  Spells[S_HEAL].powerdrain = 15;
  Spells[S_HEAL].id = S_HEAL;

  Spells[S_DISPEL].powerdrain = 40;
  Spells[S_DISPEL].id = S_DISPEL;

  Spells[S_BREATHE].powerdrain = 20;
  Spells[S_BREATHE].id = S_BREATHE;

  Spells[S_INVISIBLE].powerdrain = 15;
  Spells[S_INVISIBLE].id = S_INVISIBLE;

  Spells[S_WARP].powerdrain = 50;
  Spells[S_WARP].id = S_WARP;

  Spells[S_ENCHANT].powerdrain = 30;
  Spells[S_ENCHANT].id = S_ENCHANT;

  Spells[S_BLESS].powerdrain = 30;
  Spells[S_BLESS].id = S_BLESS;

  Spells[S_RESTORE].powerdrain = 20;
  Spells[S_RESTORE].id = S_RESTORE;

  Spells[S_CURE].powerdrain = 20;
  Spells[S_CURE].id = S_CURE;

  Spells[S_TRUESIGHT].powerdrain = 20;
  Spells[S_TRUESIGHT].id = S_TRUESIGHT;

  Spells[S_HELLFIRE].powerdrain = 90;
  Spells[S_HELLFIRE].id = S_HELLFIRE;

  Spells[S_KNOWLEDGE].powerdrain = 10;
  Spells[S_KNOWLEDGE].id = S_KNOWLEDGE;

  Spells[S_HERO].powerdrain = 20;
  Spells[S_HERO].id = S_HERO;

  Spells[S_RETURN].powerdrain = 10;
  Spells[S_RETURN].id = S_RETURN;

  Spells[S_DESECRATE].powerdrain = 50;
  Spells[S_DESECRATE].id = S_DESECRATE;

  Spells[S_HASTE].powerdrain = 15;
  Spells[S_HASTE].id = S_HASTE;

  Spells[S_SUMMON].powerdrain = 20;
  Spells[S_SUMMON].id = S_SUMMON;

  Spells[S_SANCTUARY].powerdrain = 75;
  Spells[S_SANCTUARY].id = S_SANCTUARY;

  Spells[S_ACCURACY].powerdrain = 20;
  Spells[S_ACCURACY].id = S_ACCURACY;

  Spells[S_RITUAL].powerdrain = 50;
  Spells[S_RITUAL].id = S_RITUAL;

  Spells[S_APPORT].powerdrain = 15;
  Spells[S_APPORT].id = S_APPORT;

  Spells[S_SHADOWFORM].powerdrain = 50;
  Spells[S_SHADOWFORM].id = S_SHADOWFORM;

  Spells[S_ALERT].powerdrain = 15;
  Spells[S_ALERT].id = S_ALERT;

  Spells[S_REGENERATE].powerdrain = 20;
  Spells[S_REGENERATE].id = S_REGENERATE;

  Spells[S_SANCTIFY].powerdrain = 75;
  Spells[S_SANCTIFY].id = S_SANCTIFY;

  Spells[S_CLAIRVOYANCE].powerdrain = 10;
  Spells[S_CLAIRVOYANCE].id = S_CLAIRVOYANCE;

  Spells[S_DRAIN].powerdrain = 40;
  Spells[S_DRAIN].id = S_DRAIN;

  Spells[S_LEVITATE].powerdrain = 25;
  Spells[S_LEVITATE].id = S_LEVITATE;

  Spells[S_POLYMORPH].powerdrain = 30;
  Spells[S_POLYMORPH].id = S_POLYMORPH;

  Spells[S_FEAR].powerdrain = 10;
  Spells[S_FEAR].id = S_FEAR;

  Spells[S_WISH].powerdrain = 100;
  Spells[S_WISH].id = S_WISH;

}






void cast_spell(spell)
int spell;
{
  switch(spell) {
  case S_MON_DET:s_mondet();
    break;
  case S_OBJ_DET:s_objdet();
    break;
  case S_IDENTIFY:s_identify();
    break;
  case S_FIREBOLT:s_firebolt();
    break;
  case S_SLEEP:s_sleep();
    break;
  case S_LBALL:s_lball();
    break;
  case S_TELEPORT:s_teleport();
    break;
  case S_DISRUPT:s_disrupt();
    break;
  case S_DISINTEGRATE:s_disintegrate();
    break;
  case S_MISSILE:s_missile();
    break;
  case S_HEAL:s_heal();
    break;
  case S_DISPEL:s_dispel();
    break;
  case S_BREATHE:s_breathe();
    break;
  case S_INVISIBLE:s_invisible();
    break;
  case S_WARP:s_warp();
    break;
  case S_ENCHANT:s_enchant();
    break;
  case S_BLESS:s_bless();
    break;
  case S_RESTORE:s_restore();
    break;
  case S_CURE:s_cure();
    break;
  case S_TRUESIGHT:s_truesight();
    break;
  case S_HELLFIRE:s_hellfire();
    break;
  case S_KNOWLEDGE:s_knowledge();
    break;
  case S_HERO:s_hero();
    break;
  case S_RETURN:s_return();
    break;
  case S_DESECRATE:s_desecrate();
    break;
  case S_HASTE:s_haste();
    break;
  case S_SUMMON:s_summon();
    break;
  case S_SANCTUARY:s_sanctuary();
    break;
  case S_ACCURACY:s_accuracy();
    break;
  case S_RITUAL:s_ritual();
    break;
  case S_APPORT:s_apport();
    break;
  case S_SHADOWFORM:s_shadowform();
    break;
  case S_ALERT:s_alert();
    break;
  case S_REGENERATE:s_regenerate();
    break;
  case S_SANCTIFY:s_sanctify();
    break;
  case S_CLAIRVOYANCE:s_clairvoyance();
    break;
  case S_DRAIN:s_drain();
    break;
  case S_LEVITATE:s_levitate();
    break;
  case S_FEAR:s_fear();
    break;
  case S_POLYMORPH:s_polymorph();
    break;
  case S_WISH:s_wish();
    break;
  default: mprint("Your odd spell fizzles with a small 'sput'.");
    break;
  }
}



static char *spell_names[] = {		/* alphabetical listing */
"accuracy", "alertness", "apportation", "ball lightning", "blessing",
"breathing", "clairvoyance", "curing", "desecration", "disintegrate",
"dispelling", "disrupt", "enchantment", "energy drain", "fear", "firebolt",
"haste", "healing", "hellfire", "heroism", "identification", "invisibility",
"levitate", "magic missile", "monster detection", "object detection",
"polymorph", "regeneration", "restoration", "return", "ritual magic",
"sanctification", "sanctuary", "self knowledge", "shadow form", "sleep",
"summoning", "teleport", "the warp", "true sight", "wishing" };

static int spell_ids[] = {	/* in the same order as spell_names[] */
S_ACCURACY, S_ALERT, S_APPORT, S_LBALL, S_BLESS, S_BREATHE, S_CLAIRVOYANCE,
S_CURE, S_DESECRATE, S_DISINTEGRATE, S_DISPEL, S_DISRUPT, S_ENCHANT, S_DRAIN,
S_FEAR, S_FIREBOLT, S_HASTE, S_HEAL, S_HELLFIRE, S_HERO, S_IDENTIFY,
S_INVISIBLE, S_LEVITATE, S_MISSILE, S_MON_DET, S_OBJ_DET, S_POLYMORPH,
S_REGENERATE, S_RESTORE, S_RETURN, S_RITUAL, S_SANCTIFY, S_SANCTUARY,
S_KNOWLEDGE, S_SHADOWFORM, S_SLEEP, S_SUMMON, S_TELEPORT, S_WARP, S_TRUESIGHT,
S_WISH };

void showknownspells(first, last)
int first, last;
{
  int i,printed=FALSE;

  menuclear();
  menuprint("\nPossible Spells:\n");
  for (i = first; i <= last; i++)
    if (Spells[spell_ids[i]].known) {
      printed = TRUE;
      menuprint(spell_names[i]);
      menuprint(" (");
      menunumprint(Spells[spell_ids[i]].powerdrain);
      menuprint(" mana)");
      menuprint("\n");
    }
  if (! printed)
    menuprint("\nNo spells match that prefix!");
  showmenu();
}

int spellparse()
{
  int first, last, pos;
  char byte, prefix[80];
  int found = 0;
  int f, l;

  first = 0;
  while (first < NUMSPELLS && !Spells[spell_ids[first]].known)
    first++;
  if (first == NUMSPELLS) {
    print1("You don't know any spells!");
    return ABORT;
  }
  last = NUMSPELLS - 1;
  pos = 0;
  print2("");
  do {
    byte = mgetc();
    if (byte == BACKSPACE || byte == DELETE) {
      if (pos > 0) {
        prefix[--pos] = '\0';
	byte = prefix[pos - 1];
	f = first;
	while (f >= 0 && !strncmp(prefix, spell_names[f], pos)) {
	  if (Spells[spell_ids[f]].known)
	    first = f;
	  f--;
	}
	l = last;
	while (l < NUMSPELLS && !strncmp(prefix, spell_names[l], pos)) {
	  if (Spells[spell_ids[l]].known)
	    last = l;
	  l++;
	}
	if (found)
	  found = 0;
	print2(prefix);
      }
      if (pos == 0) {
	first = 0;
	last = NUMSPELLS - 1;
	found = 0;
	print2("");
      }
    }
    else if (byte == ESCAPE) {
      xredraw();
      return ABORT;
    }
    else if (byte == '?')
      showknownspells(first, last);
    else if (byte != '\n') {
      if (byte >= 'A' && byte <= 'Z') 
	byte += 'a' - 'A';
      if (found)
	continue;
      f = first;
      l = last;
      while (f < NUMSPELLS &&
	  (!Spells[spell_ids[f]].known ||
	  strlen(spell_names[f]) < pos || spell_names[f][pos] < byte))
	f++;
      while (l >= 0 && (!Spells[spell_ids[l]].known ||
	  strlen(spell_names[l]) < pos || spell_names[l][pos] > byte))
	l--;
      if (l < f)
	continue;
      prefix[pos++] = byte;
      prefix[pos] = '\0';
      nprint2(prefix + pos - 1);
      first = f;
      last = l;
      if (first == last && !found) {	/* unique name */
	found = 1;
	nprint2(spell_names[first] + pos);
      }
    }
  } while (byte != '\n');
  xredraw();
  if (found)
    return spell_ids[first];
  else {
    print3("That is an ambiguous abbreviation!");
    return ABORT;
  }
}
